﻿using System;
using System.Collections.Generic;
using System.Threading;
using NsbExtensions.Messages.Management.Monitoring;
using NsbExtensions.Server.Management.EndpointMonitor;
using NsbExtensions.Tests.Stubs;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using ObjectBuilder;
using ObjectBuilder.SpringFramework;

namespace NsbExtensions.Tests.ServerTests
{
    [TestFixture]
    public class EndpointStatusAggregationServiceTestFixture
    {
        private TestMessagingEndpoint CreateEndpoint(IBuilder builder)
        {
            var endpoint = (TestMessagingEndpoint) TestMessagingEndpoint.Create(builder);
            endpoint.AddMessageHandlerAssembly(typeof (EndpointMonitorServer).Assembly);
            return endpoint;
        }

        private EndpointStatusAggregationService CreateAggregator(IBuilder builder, TestMessagingEndpoint endpoint)
        {
            builder.ConfigureComponent(typeof (EndpointMonitorServer), ComponentCallModelEnum.Singleton)
                .ConfigureProperty("EndpointMonitorInterval", TimeSpan.FromMinutes(10));

            builder.ConfigureComponent(typeof (EndpointStatusAggregationService), ComponentCallModelEnum.Singleton)
                .ConfigureProperty("StatusBroadcastInterval", TimeSpan.FromMinutes(10));

            builder.ConfigureComponent(typeof (TestStatusRepository), ComponentCallModelEnum.Singleton);

            var service = builder.Build<EndpointMonitorServer>();
            endpoint.AddLocalBackgroundService(service);

            var aggregator = builder.Build<EndpointStatusAggregationService>();
            endpoint.AddLocalBackgroundService(aggregator);

            return aggregator;
        }

        private EndpointStatusUpdateNotification CreateUpdate(string epId, params EndpointMeasurement[] measurements)
        {
            var notification = new EndpointStatusUpdateNotification();
            notification.EndpointID = epId;
            notification.EndpointMeasurements = new List<EndpointMeasurement>(measurements);
            return notification;
        }

        private EndpointMeasurement CreateMeasurement(string name, object value)
        {
            var m = new EndpointMeasurement
                        {
                            Name = name,
                            UnitOfMeasurement = "Unit",
                            Value = value.ToString()
                        };
            if (value is bool)
            {
                m.CanAggregate = false;
                m.DataType = MeasurementDataType.Boolean;
            }
            else if (value.GetType().IsValueType)
            {
                m.CanAggregate = true;
                m.DataType = MeasurementDataType.Numeric;
            }
            else
            {
                m.CanAggregate = false;
                m.DataType = MeasurementDataType.Text;
            }

            return m;
        }

        [Test]
        public void ProcessIncomingMessages()
        {
            var builder = new Builder().Build<IBuilder>();


            TestMessagingEndpoint endpoint = CreateEndpoint(builder);
            EndpointStatusAggregationService svc = CreateAggregator(builder, endpoint);
            var bus = (TestBus) endpoint.Bus;
            bus.PublishedMessages.Clear();

            endpoint.Start();

            svc.EndpointMonitor.UpdateEndpointStatus(CreateUpdate("testEp1", CreateMeasurement("testM1", 2),
                                                                  CreateMeasurement("testM2", false)));
            svc.EndpointMonitor.UpdateEndpointStatus(CreateUpdate("testEp2", CreateMeasurement("testM3", 5.5),
                                                                  CreateMeasurement("testM4", "hello")));
            Thread.Sleep(50);

            var r = (TestStatusRepository) builder.Build<IEndpointStatusRepository>();
            Assert.That(r.StatusList.Count, Is.EqualTo(2));

            svc.StatusBroadcastInterval = TimeSpan.FromMilliseconds(50);
            Thread.Sleep(100);

            Assert.That(bus.PublishedMessages.Count, Is.GreaterThan(0));
            foreach (SentMessage message in bus.PublishedMessages)
            {
                Assert.That(message.Message, Is.TypeOf(typeof (EndpointStatusSummaryNotification)));
            }

            var summary = bus.LastPublished.Message as EndpointStatusSummaryNotification;
            Assert.IsNotNull(summary);
            Assert.That(summary.ActiveEndpoints.Count, Is.EqualTo(2));

            svc.EndpointMonitor.UpdateEndpointStatus(new EndpointOfflineNotification {EndpointID = "testEp2"});

            Thread.Sleep(100);

            summary = bus.LastPublished.Message as EndpointStatusSummaryNotification;
            Assert.IsNotNull(summary);
            Assert.That(summary.ActiveEndpoints.Count, Is.EqualTo(1));

            svc.EndpointMonitor.UpdateEndpointStatus(CreateUpdate("testEp1", CreateMeasurement("testM1", 1),
                                                                  CreateMeasurement("testM2", false)));
            svc.EndpointMonitor.UpdateEndpointStatus(CreateUpdate("testEp2", CreateMeasurement("testM3", 10.5),
                                                                  CreateMeasurement("testM4", "hello")));

            Thread.Sleep(100);

            summary = bus.LastPublished.Message as EndpointStatusSummaryNotification;
            Assert.IsNotNull(summary);
            Assert.That(summary.ActiveEndpoints.Count, Is.EqualTo(2));

            Assert.That(summary.ActiveEndpoints[0].StatusMeasurements.Count, Is.EqualTo(2));
            Assert.That(summary.ActiveEndpoints[0].StatusMeasurements[0].Average, Is.EqualTo(1.5));
            Assert.That(summary.ActiveEndpoints[0].StatusMeasurements[1].Average, Is.EqualTo(0.0));
            Assert.That(summary.ActiveEndpoints[1].StatusMeasurements[0].Average, Is.EqualTo(10.5));
            Assert.That(summary.ActiveEndpoints[1].StatusMeasurements[1].LastMeasurement.Value, Is.EqualTo("hello"));
            Assert.That(summary.ActiveEndpoints[1].StatusMeasurements[1].Average, Is.EqualTo(0.0));

            endpoint.Stop();
        }

        [Test]
        public void StartStopTest()
        {
            IBuilder builder = new Builder();

            TestMessagingEndpoint endpoint = CreateEndpoint(builder);
            EndpointStatusAggregationService svc = CreateAggregator(builder, endpoint);


            endpoint.Start();
            Assert.IsTrue(svc.IsRunning);
            endpoint.Stop();
            Assert.IsFalse(svc.IsRunning);
        }
    }
}